package se.rende.myrss.data;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.transform.TransformerConfigurationException;

import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;

import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Text;

public class DataService {
	private Map<String, FilterType> filterTypeMap = new HashMap<String, FilterType>();
	private final Map<String, Filter> filterMap = new HashMap<String, Filter>();
	private final List<ChannelFilter> channelFilters = new ArrayList<ChannelFilter>();
	
	public DataService() throws TransformerConfigurationException, SAXNotRecognizedException, SAXNotSupportedException {
		addFilterType(new RegexFilterType()); 
		addFilterType(new IncludeSlashdotCommentsFilterType());
		
		addFilter("regex", "rmslashads", "remove slashdot ads", Arrays.asList("<a href=\"http://feedads\\.g\\.doubleclick\\.net/.*\"><img src=\"http://feedads\\.g\\.doubleclick\\.net/.*\" .*></img></a>", " "));
		addFilter("regex", "rmtwbutton", "remove twitter button", Arrays.asList("<a class=\"slashpop\" href=\"http://twitter\\.com/home\\?status=.*\"><img src=\"http://a\\.fsdn\\.com/sd/twitter_icon_large\\.png\"></a>", " "));
		addFilter("regex", "rmfblikebutton", "remove like button", Arrays.asList("<a class=\"slashpop\" href=\"http://www\\.facebook\\.com/sharer\\.php\\?u=.*\"><img src=\"http://a\\.fsdn\\.com/sd/facebook_icon_large\\.png\"></a>", " "));
		addFilter("regex", "rmgglbutton", "remove google +1 button", Arrays.asList("<a class=\"nobg\" href=\"http://plus\\.google\\.com/.*\" onclick=\"javascript:.*\"><img src=\"http://www\\.gstatic\\.com/images/icons/gplus-16\\.png\" alt=\"Share on Google\\+\"/></a>", ""));
		addFilter("regex", "rmslashdotfooter", "remove slashdot go to article footer", Arrays.asList("<p><div class=\"share_submission\" style=\"position:relative;\">", ""));
		addFilter("regex", "rmslashdotcomments", "remove slashdot comments", Arrays.asList("</div></p><p><a href=\"http://.*\\.slashdot\\.org/.*\">Read more of this story</a> at Slashdot\\.</p><iframe src=\".*\" style=\".*\" height=\"1\" width=\"1\"/>", ""));

		addFilter("inclsdcomments", "inclsdcomments", "include slashdot comments", null);
		
		String slashdotUrl = "http://rss.slashdot.org/Slashdot/slashdot";
		String swedroidUrl = "http://feeds.feedburner.com/swedroid-se";
		String lxerUrl = "http://lxer.com/module/newswire/headlines.rss";
		
		addChannelFilter(slashdotUrl, "rmslashads");
		addChannelFilter(slashdotUrl, "rmtwbutton");
		addChannelFilter(slashdotUrl, "rmfblikebutton");
		addChannelFilter(slashdotUrl, "rmgglbutton");
		addChannelFilter(slashdotUrl, "rmslashdotfooter");
		addChannelFilter(slashdotUrl, "rmslashdotcomments");
		addChannelFilter(slashdotUrl, "rmslashdotcomments");
		addChannelFilter(slashdotUrl, "inclsdcomments");
		
//		addChannel("Slashdot", slashdotUrl);
//		addChannel("Swedroid", swedroidUrl);
//		addChannel("LXer", lxerUrl);
	}

	private void addChannelFilter(String channelUrl, String filterId) {
		channelFilters.add(new ChannelFilter(channelUrl, filterMap.get(filterId)));
	}

	private void addFilterType(FilterType filterType) {
		filterTypeMap.put(filterType.getId(), filterType);
	}

	private void addFilter(String typeId, String id, String name,	List<String> args) {
		Filter filter = new Filter(id, name, filterTypeMap.get(typeId), args);
		filterMap.put(filter.getId(), filter);
	}

	public Collection<FilterType> getFilterTypes() {
		return filterTypeMap.values();
	}

	public Collection<Filter> getFilters() {
		return filterMap.values();
	}

	public List<ChannelFilter> getChannelFilters() {
		return channelFilters;
	}

	public List<Filter> getFiltersByUrl(String url) {
		List<Filter> filters = new ArrayList<Filter>();
		for (ChannelFilter channelFilter : channelFilters) {
			if (url.equals(channelFilter.getUrl())) {
				filters.add(channelFilter.getFilter());
			}
		}
		return filters;
	}

	public long addChannel(String name, String url) {
        Entity channel = new Entity("Channel");
        channel.setProperty("name", name);
        channel.setProperty("url", url);
        return DatastoreServiceFactory.getDatastoreService().put(channel).getId();
	}

	public List<Channel> getChannels() {
	    Query query = new Query("Channel").addSort("name", Query.SortDirection.DESCENDING);
	    List<Entity> channelEntities = DatastoreServiceFactory.getDatastoreService().prepare(query).asList(FetchOptions.Builder.withLimit(5));
	    List<Channel> channels = new ArrayList<Channel>();
	    for (Entity channelEntity : channelEntities) {
			channels.add(new Channel(channelEntity.getKey().getId(), (String)channelEntity.getProperty("name"), (String)channelEntity.getProperty("url")));
		}
	    return channels;
	}

	public void deleteChannel(long id) {
		System.out.println("DataService.deleteChannel(" + id + ")");
		DatastoreServiceFactory.getDatastoreService().delete(KeyFactory.createKey("Channel", id));
		System.out.println("DataService.deleteChannel() ready");
	}

	/**
	 * The latest date for the items in the database for this channel.
	 * @param channel
	 * @return the latest date or new Date(0L) if no items
	 */
	public Date getLatestItemDate(Channel channel) {
	    Query query = new Query("Item", KeyFactory.createKey("Channel", channel.getId())).addSort("date", Query.SortDirection.DESCENDING);
	    List<Entity> latestItems = DatastoreServiceFactory.getDatastoreService().prepare(query).asList(FetchOptions.Builder.withLimit(1));
	    if (latestItems == null || latestItems.size() == 0) {
	    	return new Date(0L);
	    }
	    return (Date) latestItems.get(0).getProperty("date");
	}

	/**
	 * Creates and stores a new item for the supplied channel and with the supplied properties.
	 * 
	 * @param channel identifies the parent channel by its id
	 * @param title item title
	 * @param link item web link
	 * @param publishedDate publish date for the item
	 * @param author item author
	 * @param description item desctiption tex - can be long
	 * @return the id of the stored item
	 */
	public long addChannelItem(Channel channel, String title, String link, Date publishedDate,
			String author, String description) {
		System.out.println("DataService.addChannelItem('" + channel.getName() + "', '" + title + "')");
        Entity item = new Entity("Item", KeyFactory.createKey("Channel", channel.getId()));
        item.setProperty("title", title);
        item.setProperty("link", link);
        item.setProperty("date", publishedDate);
        item.setProperty("author", author);
        item.setProperty("description", new Text(description));
        return DatastoreServiceFactory.getDatastoreService().put(item).getId();
	}

	public List<ChannelItem> getItems(long channelId) {
		List<ChannelItem> items =  new ArrayList<ChannelItem>();
	    Query query;
		if (channelId != 0) {
			query = new Query("Item", KeyFactory.createKey("Channel", channelId)).addSort("date", Query.SortDirection.DESCENDING);
		} else {
			query = new Query("Item").addSort("date", Query.SortDirection.DESCENDING);
		}
	    for (Entity entity : DatastoreServiceFactory.getDatastoreService().prepare(query).asIterable()) {
			items.add(new ChannelItem(
					entity.getKey().getId(), 
					entity.getKey().getParent().getId(),
					(String)entity.getProperty("title"),
					(String)entity.getProperty("link"),
					(Date)entity.getProperty("date"),
					(String)entity.getProperty("author"),
					((Text)entity.getProperty("description")).getValue()));
		}
		return items;
	}

}
